home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 June / EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso / earcd / cmdity / clicker.lha / Clicker / Source / main.c < prev    next >
C/C++ Source or Header  |  1996-05-17  |  10KB  |  369 lines

  1. /*  File:         main.c
  2.  *  Created:      01-01-95
  3.  *  Updated:      17-05-96
  4.  *  Version:      1.3
  5.  *  Project:      Clicker
  6.  *  Owner:        Jeroen Vermeulen
  7.  *  Requirements: KickStart V39+
  8.  *  Legal:        PD
  9.  *  Status:       Release
  10.  */
  11.  
  12. char verstr[]="$VER: Clicker 1.3 (17.05.96) Jeroen Vermeulen",
  13.      descrp[]="Clicker 1.3 - Public Domain";
  14.  
  15. /* TODO:
  16.  *      Prefs save option!
  17.  *      Add note names to frequency display
  18.  */
  19.  
  20. #include <proto/exec.h>
  21. #include <exec/libraries.h>
  22. #include <exec/devices.h>
  23. #include <exec/memory.h>
  24. #include <proto/dos.h>
  25. #include <proto/icon.h>
  26. #include <proto/utility.h>
  27. #include <proto/commodities.h>
  28. #include <proto/gadtools.h>
  29. #include <proto/alib.h>
  30. #include <intuition/gadgetclass.h>
  31.  
  32. #include "main.h"
  33. #include "broker.h"
  34. #include "prefs.h"
  35. #include "sound.h"
  36. #include "gui.h"
  37.  
  38.  
  39. STRPTR  AllocFailMsgPort = "Unable to set up message port!\n",
  40.         OpenFailIconBase = "Need icon.library V36 or better\n",
  41.         OpenFailUtilBase = "Couldn't open utility.library\n",
  42.         OpenFailCommBase = "Couldn't open commodities.library\n",
  43.         OpenFailGadTBase = "Couldn't open gadtools.library\n";
  44.  
  45. static STRPTR Break_Exit = "**BREAK** -- Clicker removed\n";
  46.  
  47. struct UTILITYBASE *UtilityBase=NULL;
  48. struct Library *CxBase=NULL, *GadToolsBase=NULL;
  49.  
  50. static BOOL SetupLibs(STRPTR *const errptr)
  51. {
  52.   UtilityBase  = (struct UTILITYBASE *)OpenLibrary("utility.library",0);
  53.   if (!UtilityBase)  *errptr = OpenFailUtilBase;
  54.   CxBase       = OpenLibrary("commodities.library",0);
  55.   if (!CxBase)       *errptr = OpenFailCommBase;
  56.   GadToolsBase = OpenLibrary("gadtools.library",0);
  57.   if (!GadToolsBase) *errptr = OpenFailGadTBase;
  58.   return (BOOL)(UtilityBase && CxBase && GadToolsBase);
  59. }
  60.  
  61. static void CleanupLibs(void)
  62. {
  63.   CloseLibrary((struct Library *)UtilityBase);
  64.   CloseLibrary(CxBase);
  65.   CloseLibrary(GadToolsBase);
  66. }
  67.  
  68.  
  69. /* EventLoop():
  70.  * This is where our commodity does its work. This function receives
  71.  * CX messages and acts on them, mostly by making noises.
  72.  * If anything should go wrong, errptr is directed to an error string
  73.  * and the return value is set accordingly. If all is well, this should
  74.  * be RETURN_OK.
  75.  */
  76. static int EventLoop(STRPTR                *const errptr,
  77.                      CxObj                 *const brokerobj,
  78.                      struct MsgPort        *const cxport,
  79.                      struct IOAudio        *const soundrequest,
  80.                      struct WindowContext  *const PrefsWin)
  81. {
  82.   const ULONG  waitmask = SIGBREAKF_CTRL_C | (1<<(cxport->mp_SigBit));
  83.   /* --- */
  84.  
  85.   for (;;)
  86.   {
  87.     struct Message *msg;
  88.     enum { win_unchanged, win_open, win_close } dowindow = win_unchanged;
  89.     const  ULONG    gotsig = Wait(waitmask|(PrefsWin->SigMask));
  90.  
  91.     /* --- */
  92.  
  93.     if (gotsig & SIGBREAKF_CTRL_C)
  94.     {
  95.       *errptr = Break_Exit;
  96.       return(RETURN_WARN);
  97.     }
  98.  
  99.     while ((msg = GetMsg(cxport)))
  100.     {
  101.       CxMsg *const cxmsg = (CxMsg *)msg;
  102.       ULONG msgid    = CxMsgID(cxmsg);
  103.       ULONG msgtype  = CxMsgType(cxmsg);
  104.       BOOL  clicknow = FALSE;
  105.  
  106.       switch (msgtype)
  107.       {
  108.         case CXM_COMMAND: switch (msgid)
  109.           {
  110.             case CXCMD_DISABLE:
  111.                   ActivateCxObj(brokerobj,FALSE);
  112.             break;
  113.  
  114.             case CXCMD_ENABLE:
  115.                   ActivateCxObj(brokerobj,TRUE);
  116.             break;
  117.  
  118.             case CXCMD_KILL:
  119.                   return(RETURN_OK);
  120.  
  121.             case CXCMD_APPEAR:
  122.             case CXCMD_UNIQUE:
  123.                   dowindow = win_open;
  124.             break;
  125.  
  126.             case CXCMD_DISAPPEAR:
  127.                   dowindow = win_close;
  128.             break;
  129.           }
  130.         break;
  131.  
  132.         case CXM_IEVENT:
  133.         {
  134.           const struct InputEvent *const ievent =
  135.                 (struct InputEvent *)CxMsgData(cxmsg);
  136.  
  137.           if (!(ievent->ie_Code & IECODE_UP_PREFIX))
  138.           {
  139.             switch (ievent->ie_Class)
  140.             {
  141. #ifndef   NOCLICKMOUSE
  142.               case IECLASS_RAWMOUSE:
  143.                 clicknow = ClickPrefs.ClickMouse;
  144.               break;
  145. #endif /* NOCLICKMOUSE */
  146.  
  147.               case IECLASS_RAWKEY:
  148.                 clicknow = TRUE;
  149.               break;
  150.  
  151.               default:
  152.               break;
  153.             }
  154.           }
  155.         }
  156.         break;
  157.  
  158.         default:
  159.         break;
  160.       }
  161.       ReplyMsg(msg);
  162.       if (clicknow) KeyClick(soundrequest);
  163.     }
  164.     if (PrefsWin->Shown)
  165.     {
  166.       struct IntuiMessage *imsg;
  167.       /* --- */
  168.       while ((imsg = GT_GetIMsg(PrefsWin->Win->UserPort)))
  169.       {
  170.         const ULONG imsg_Class = imsg->Class;
  171.         const UWORD imsg_Code  = imsg->Code;
  172.         const struct Gadget *const g = imsg->IAddress;
  173.  
  174.         /* --- */
  175.  
  176.         GT_ReplyIMsg(imsg);
  177.  
  178.         switch (imsg_Class)
  179.         {
  180.           case IDCMP_GADGETDOWN:
  181.           case IDCMP_GADGETUP:
  182.           case IDCMP_MOUSEMOVE:
  183.  
  184.             /* Note that this *looks* like a race condition;  I don't think it
  185.              * is because we own the gadget, therefore it can only go away if
  186.              * we tell it to.  Its GadgetID is supposed to remain constant as
  187.              * well so there can be no change to the GadgetID.
  188.              * Note that Intuition will not close the window before all
  189.              * intuimessages have been replied, so message arrival should not
  190.              * span reincarnations of our window.
  191.              */
  192.             switch (g->GadgetID)
  193.             {
  194.               case mygadget_volume:
  195.                 ClickPrefs.volume = imsg_Code;
  196.                 ClickPrefs.newsettings = TRUE;
  197.               break;
  198.  
  199.               case mygadget_cycles:
  200.                 ClickPrefs.cycles = imsg_Code;
  201.                 ClickPrefs.newsettings = TRUE;
  202.               break;
  203.  
  204.               case mygadget_period:
  205.                 ClickPrefs.period = SliderToPeriod(imsg_Code);
  206.                 ClickPrefs.newsettings = TRUE;
  207.               break;
  208.  
  209. #ifndef   NOCLICKMOUSE
  210.               case mygadget_clickmouse:
  211.                 /* Another potential race condition; but this is one that
  212.                  * IMO always invokes the desired behaviour.  It occurs when
  213.                  * the ClickMouse gadget is changed so fast in succession that
  214.                  * its state is altered before we've processed the previous
  215.                  * message.  In that case the new state is used immediately.
  216.                  */
  217.                 ClickPrefs.ClickMouse = (g->Flags & GFLG_SELECTED);
  218.               break;
  219. #endif /* NOCLICKMOUSE */
  220.  
  221.               default:
  222.               break;
  223.             }
  224.           break;
  225.  
  226.           case IDCMP_CLOSEWINDOW:
  227.             dowindow = win_close;
  228.           break;
  229.  
  230.           case IDCMP_REFRESHWINDOW:
  231.              GT_BeginRefresh(PrefsWin->Win);
  232.              GT_EndRefresh(PrefsWin->Win,TRUE);
  233.           break;
  234.  
  235.           default:
  236.           break;
  237.         }
  238.       }
  239.     }
  240.     if (dowindow != win_unchanged)
  241.     {
  242.       if (dowindow == win_open)
  243.       {
  244.         ShowWindow(errptr,PrefsWin);
  245.         if (*errptr) return RETURN_FAIL;
  246.       }
  247.       else HideWindow(PrefsWin);
  248.     }
  249.   }
  250. }
  251.  
  252.  
  253. /* MatchStr():
  254.  * Boolean frontend for stricmp().  Returns TRUE if the strings pointed to by
  255.  * its arguments are identical (case-insensitive comparison), or FALSE if not.
  256.  */
  257. static BOOL MatchStr(const STRPTR a, const STRPTR b)
  258. {
  259.   return (BOOL)(Stricmp((UBYTE *)a,(UBYTE *)b)==0);
  260. }
  261.  
  262. /* BoolAttr():
  263.  * Returns whether a tooltype string should be interpreted as TRUE.  Defaults to
  264.  * FALSE if no string is given, but TRUE if the string is empty.
  265.  * Hopefully this behaviour allows tooltypes to be set without arguments.
  266.  */
  267. static BOOL BoolAttr(const UBYTE *const value)
  268. {
  269.   UBYTE *val = (UBYTE *)value;
  270.   if (val && (!*val || MatchStr(val,"TRUE") || MatchStr(val,"YES") ||
  271.       MatchStr(val,"ON")))
  272.     return TRUE;
  273.   else return FALSE;
  274. }
  275.  
  276.  
  277. int main(LONG argc, UBYTE **argv)
  278. {
  279.   int      returnval = RETURN_FAIL;
  280.   STRPTR   error  = NULL;
  281.   BYTE     CX_Priority = 0;
  282.   BOOL     cx_popup = FALSE;
  283.  
  284.   struct NewBroker *mybroker;
  285.   struct IOAudio   *soundrequest;
  286.  
  287.   /* --- */
  288.  
  289.   if (!SetupLibs(&error))
  290.   {
  291.     CleanupLibs();
  292.     if (error) PutStr(error);
  293.     return RETURN_FAIL;
  294.   }
  295.  
  296.   ClickPrefs.period = HertzToPeriod(739);
  297.   ClickPrefs.volume = 50;
  298.   ClickPrefs.cycles = 1;
  299.  
  300.   soundrequest = CreateSample(&error);
  301.   if (soundrequest)
  302.   {
  303.     if ((IconBase = OpenLibrary("icon.library",36)))
  304.     {
  305.       UBYTE **ToolTypes;
  306.       /* --- */
  307.       if ((ToolTypes=ArgArrayInit(argc,argv)))
  308.       {
  309.         /* Read tooltype settings for click sound.  May be buggy (!?)
  310.          */
  311.         ClickPrefs.period = HertzToPeriod(ArgInt(ToolTypes,"PITCH",739));
  312.         ClickPrefs.volume = ArgInt(ToolTypes,"VOLUME",50);
  313.         ClickPrefs.cycles = ArgInt(ToolTypes,"LENGTH",1);
  314.  
  315. #ifdef    NOCLICKMOUSE
  316.         ClickPrefs.ClickMouse = FALSE;
  317. #else  /* NOCLICKMOUSE */
  318.         ClickPrefs.ClickMouse = BoolAttr(ArgString(ToolTypes,"CLICKMOUSE","NO"));
  319. #endif /* NOCLICKMOUSE */
  320.  
  321.         cx_popup             = BoolAttr(ArgString(ToolTypes,"CX_POPUP","NO"));
  322.         ArgArrayDone();
  323.       }
  324.       CloseLibrary(IconBase);
  325.     }
  326.     else
  327.     {
  328.       DeleteSample(soundrequest);
  329.       PutStr(OpenFailIconBase);
  330.       return RETURN_FAIL;
  331.     }
  332.  
  333.     /* A duration of zero means "infinite" to audio.device. */
  334.     if (!ClickPrefs.cycles) ClickPrefs.cycles = 1;
  335.     ClickPrefs.newsettings = TRUE;
  336.  
  337.     mybroker = MakeBroker(&error, CX_Priority);
  338.     if (mybroker)
  339.     {
  340.       CxObj  *brokerobj;
  341.       /* --- */
  342.       if ((brokerobj = SetupBroker(&error,mybroker)))
  343.       {
  344.         struct WindowContext *PrefsWin;
  345.         /* --- */
  346.  
  347.         if ((PrefsWin = MakeWindow(&error,cx_popup)))
  348.         {
  349.           ActivateCxObj(brokerobj,TRUE);
  350.           returnval = EventLoop(&error, brokerobj, mybroker->nb_Port,
  351.                                 soundrequest, PrefsWin);
  352.           ActivateCxObj(brokerobj,FALSE);
  353.           DestroyWindow(PrefsWin);
  354.         }
  355.         DeleteCxObjAll(brokerobj);
  356.       }
  357.       KillBroker(mybroker);
  358.     }
  359.     DeleteSample(soundrequest);
  360.   }
  361.  
  362.   if (error)
  363.   {
  364.     PutStr(error);
  365.   }
  366.   CleanupLibs();
  367.   return(returnval);
  368. }
  369.